package edu.ec.tesis.prototiporecomendador.ngc.Recomendadores.Semantico;

import java.util.ArrayList;
import java.util.Iterator;



import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import edu.ec.tesis.prototiporecomendador.ngc.UserProfile.Configuration;
import edu.ec.tesis.prototiporecomendador.ngc.UserProfile.ConnectionDB;

public class Inferencia {

    /**
     * Metodo encargado de inferir el DOI de un contenido audiovisual para un
     * usuario dado, empleando las secuancias de propiedades relevantes para el
     * mismo y utilizando las asociaciones rho-path y rho-join
     *
     * @param user perfil-ontologia del usuario, de donde se extraeran las
     * secuencias de propiedades
     * @param filtradas lista de secuencias de propiedades relevantes para el
     * usuario
     * @param movie contenido al que se quiere inferir el DOI
     * @param idUser id del usuario en cuestion
     * @param conf configuracion del sistema
     * @return valor del DOI inferido
     */
    public static float inferedDOI(OntModel user, ArrayList<PropertySequence> filtradas, Individual movie, int idUser, Configuration conf, boolean esUsuario) {

        //obtengo las asociaciones rho-path de las secuencias de propiedades filtradas		
        ArrayList<PropertySequence> asociacionesRhoPath = rhoPath(filtradas);

        //inicializo variables para el promedio ponderado
        float numerador = 0;
        float denominador = 0;

        //recorro las asociaciones		
        Iterator<PropertySequence> i = asociacionesRhoPath.iterator();
        while (i.hasNext()) {
            PropertySequence sequence = i.next();
            float largo = sequence.length(); //obtengo el largo de la secuencia
            Individual peliculaFinal = sequence.getNode(sequence.length() - 1); //pelicula que es el final de la secuencia					

            //obtengo la propiededad hasDOI de la pelicula final y agrego la informacion a la ecuacion
            DatatypeProperty hasDOI = user.getDatatypeProperty(conf.getNS() + "hasDOI");
            RDFNode prop = peliculaFinal.getPropertyValue(hasDOI);
            // System.out.println(peliculaFinal.toString());
            if (prop != null) {

                float doi = prop.asLiteral().getInt();
                float aux = doi / largo;
                numerador = numerador + aux;
                float aux2 = 1 / largo;
                denominador = denominador + aux2;
            }
        }

        //obtengo el rho-join, que es el aporte de los generos de la pelicula
        float join = rhoJoin(movie, idUser, conf, esUsuario);
        if (join > 0) {
            //el largo de una asociacion rho-join es de 3, porque son asociaciones de una sola propiedad
            float largoGeneros = 3;
            float aux = join / largoGeneros;
            numerador = numerador + aux;
            float aux2 = 1 / largoGeneros;
            denominador = denominador + aux2;
        }

        float[] generos = rhoJoin2(movie, idUser, conf, esUsuario);
        float largoGeneros = 3;
        for (int j = 0; j < generos.length; j++) {
            float aux = generos[j] / largoGeneros;
            numerador = numerador + aux;
            float aux2 = 1 / largoGeneros;
            denominador = denominador + aux2;
        }

        if (numerador > 0 && denominador > 0) {
            //el resultado final es un promedio ponderado de las preferencias que se relacionan a travez de los phipath con la movie a inferir
            return numerador / denominador;
        } else {
            return 0;
        }
    }

    /**
     * Metodo encargado de obtener las asociaciones rho-join, que es el aporte
     * de los generos de la pelicula
     *
     * @param movie contenido al que se quiere inferir el DOI
     * @param idUser id del usuario en cuestion
     * @param conf configuracion del sistema
     * @return valor de los rho-join
     */
    public static float rhoJoin(Individual movie, int idUser, Configuration conf, boolean usuario) {
        //se obtienen las clases padre de la pelicula
        ExtendedIterator<OntClass> i = movie.listOntClasses(true);

        //inicializo las variables para hallar el promedio
        float doi = 0;
        int count = 0;
        boolean five = false;
        boolean one = false;
        while (i.hasNext()) {
            OntClass clase = i.next();
            String tabla = clase.getLocalName();
            String user;
            if (!usuario) {
                user = "s" + idUser;
            }
            else
            {
                user=String.valueOf(idUser);
            }
            float result = ConnectionDB.select(conf.getURL(), conf.getDBNAME(), conf.getDBUSER(), conf.getDBPASS(), tabla, user);
            if (result >= 4.5) {
                five = true;
            }
            if (result <= 1.5) {
                one = true;
            }
            doi = doi + result;
            count++;
        }

        if (five && !one) {
            return 5;
        } else if (one && !five) {
            return 1;
        } else {
            return doi / count;
        }
    }

    public static float[] rhoJoin2(Individual movie, int idUser, Configuration conf, boolean usuario) {
        //se obtienen las clases padre de la pelicula
        ExtendedIterator<OntClass> i = movie.listOntClasses(true);

        ArrayList<Float> lista = new ArrayList<Float>();
        while (i.hasNext()) {
            OntClass clase = i.next();
            String tabla = clase.getLocalName();
             String user;
            if (!usuario) {
                user = "s" + idUser;
            }
            else
            {
                user=String.valueOf(idUser);
            }
            float result = ConnectionDB.select(conf.getURL(), conf.getDBNAME(), conf.getDBUSER(), conf.getDBPASS(), tabla, user);
            lista.add(result);
        }

        float[] lista2 = new float[lista.size()];
        for (int j = 0; j < lista.size(); j++) {
            lista2[j] = lista.get(j).floatValue();
        }

        return lista2;
    }

    /**
     * Encuentra las asociaciones rho-path de las secuencias de propiedades
     *
     * @param filtradas lista de secuencias de propiedades relevantes para el
     * usuario
     * @return asociaciones rho-path
     */
    private static ArrayList<PropertySequence> rhoPath(ArrayList<PropertySequence> filtradas) {
        ArrayList<PropertySequence> asociaciones = new ArrayList<PropertySequence>();
        Iterator<PropertySequence> i = filtradas.iterator();
        //recorro las secuencias de propiedades
        while (i.hasNext()) {
            PropertySequence sequence = i.next();
            ArrayList<Individual> arrayRes = sequence.getSequence(); //obtengo la secuencia de instancias
            Iterator<Individual> j = arrayRes.iterator();
            //recorro la secuencia en busca de asociaciones rho-path
            int count = 1;//creo un contador para evitar tomar la primer pelicual como asociacion rho-path
            while (j.hasNext()) {
                Individual individual = j.next();
                OntClass clase = individual.getOntClass();	//obtengo la clase padre de la instancia
                OntClass superclass = clase.getSuperClass();
                if (superclass.getLocalName().equals("ContentCS")) {
                    //si no es la primer pelicula agrego la asociacion
                    if (count != 1) {
                        Individual aux = individual;
                        //obtengo la subsecuencia con final en la pelicula aux
                        PropertySequence subsequence = sequence.getSubSequence(sequence.getIndex(aux));
                        asociaciones.add(subsequence);
                    }
                    count++;
                }
            }
        }

        //elimino las asociaciones repetidas, si es que las hay
        asociaciones = eliminarRepetidas(asociaciones);

        return asociaciones;
    }

    /**
     * Metodo encargado de eliminar asociaciones semanticas repetidas de una
     * lista
     *
     * @param asociaciones semanticas
     * @return asociaciones seamnticas filtradas
     */
    private static ArrayList<PropertySequence> eliminarRepetidas(ArrayList<PropertySequence> asociaciones) {
        ArrayList<PropertySequence> filtradas = new ArrayList<PropertySequence>();

        //recorro las asociaciones en busca de repetidas
        for (int i = 0; i < asociaciones.size(); i++) {
            PropertySequence pivot = asociaciones.get(i);
            //busco si esta duplicada
            boolean sonIguales = false;
            for (int j = i + 1; j < asociaciones.size(); j++) {
                boolean equals = pivot.equals(asociaciones.get(j));
                if (equals) {
                    sonIguales = equals;
                }
            }
            //si NO esta duplicada la agrego
            if (!sonIguales) {
                filtradas.add(pivot);
            }
        }

        return filtradas;

    }

}
